package xy;

import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.XMLConstants;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import org.w3c.dom.NodeList;

import java.io.File;
import java.io.IOException;  
import java.net.URL;

import javax.xml.parsers.*;  
import javax.xml.xpath.*;  

import org.dom4j.DocumentException;
import org.dom4j.io.SAXReader;
import org.w3c.dom.*;  
import org.xml.sax.SAXException; 

public class readXML {

static SAXReader reader = new SAXReader();

String msg;//反馈提示消息
String path = readXML.class.getClassLoader().getResource("../../book.xml").getPath();//获取相对路径，xml文件在服务器开启时会同步到服务器目录下，所以需要获取服务器目录路径
String xsdPath = readXML.class.getClassLoader().getResource("../../book.xsd").getPath();
static int size = 13;//限制每页显示数
int countE;
static Document doc ;
static Element root ;
boolean f = false;

	public int countElement() {
		
		try {
			doc = reader.read(new File(path));
			root = doc.getRootElement();
		} catch (DocumentException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		
		countE = root.elements().size();
		countE = countE%size==0?countE/size:countE/size+1;
		return countE;
	}
	
//dom4j获取所有节点
	public ArrayList<Student> dis(int currentPage){
		
		try {
			doc = reader.read(new File(path));
			root = doc.getRootElement();
		} catch (DocumentException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		
		
		ArrayList<Student> stus = new ArrayList<Student>();
		
		List<Element> nodes = root.elements("学生成绩");
		
		for(int i=(currentPage-1)*size;i<((currentPage-1)*size+size)&&i<nodes.size();i++){
			//每页显示数，所以只获取size个节点
			Element ele = nodes.get(i);
			List<Element> eles = ele.elements();
			Student stu = new Student();
			for(Element e:eles){
				if(e.getName().equals("学号")){
					stu.setId(e.getTextTrim());
				}else if(e.getName().equals("姓名")){
					stu.setName(e.getTextTrim());
				}else if(e.getName().equals("JSP")){
					stu.setJSP(e.getTextTrim());
				}else if(e.getName().equals("XML")){
					stu.setXML(e.getTextTrim());
					stus.add(stu);
				}
			}
			
		}
//		System.out.println(stus.size());
		return stus ;
	}
	
	//删除函数。dom4j定位学号之类信息节点判断，若是该节点则返回上个节点删除
	public void del(String tag,String text) {  
		try {
			doc = reader.read(new File(path));
			root = doc.getRootElement();
			
		} catch (DocumentException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		
        List<Element> nodes = root.elements("学生成绩"); //学生成绩节点
        for(Element ele:nodes){
        	List<Element> eles = ele.elements();//学生成绩下各信息节点
        	for(Element e:eles){
				if(e.getName().equals(tag)){
					if(e.getTextTrim().equals(text)){
						root.remove(ele);
					}
				}
			}
        	
        }
        //删除节点  
                
        //删除后将xml重新写入
        OutputFormat out = new OutputFormat("  ",true);
        out.setEncoding("UTF-8");
		
		try {
			Writer wr = new FileWriter(new File(path));
			XMLWriter xwr = new XMLWriter(wr,out);
			xwr.write(doc);
			wr.close();
			xwr.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}    
        
    }

	//添加函数，dom4j技术，首先写入将添加内容写入xml，再利用schema验证，若scheme验证通过则添加成功，若schema报错则为不符规范，删除添加的节点返回不符规范msg
	public String insert(Student stu) {
		// TODO Auto-generated method stub
		try {
			Document doc = reader.read(new File(path));
			Element root = doc.getRootElement();
			
			List<Element> nodes = root.elements("学生成绩"); //学生成绩节点
	        for(Element ele:nodes){
	        	List<Element> eles = ele.elements();//学生成绩下各信息节点
	        	for(Element e:eles){
					if(e.getName().equals("学号")){
						if(e.getTextTrim().equals(stu.getId())){//判断学号是否存在
							f = true;
							msg = "学号不能重复";
							return msg;
						}
					}
	        	}
	        }
			
	        if(!f){
				Element lroot = root.addElement("学生成绩");
			    Element id = lroot.addElement("学号");
				id.setText(stu.getId());
				Element name = lroot.addElement("姓名");
				name.setText(stu.getName());
				Element jsp = lroot.addElement("JSP");
				jsp.setText(stu.getJSP());
				Element xml = lroot.addElement("XML");
				xml.setText(stu.getXML());
			    OutputFormat out = new OutputFormat("  ",true);
			    out.setEncoding("UTF-8");
					try {
						Writer wr = new FileWriter(new File(path));
						XMLWriter xwr = new XMLWriter(wr,out);
						xwr.write(doc);
						wr.close();
						xwr.close();
					} catch (IOException e) {
						// TODO Auto-generated catch block
						System.out.println("新增时"+e);
					}
					
					//判断写入的信息是否符合schema约束规范
					if(!validateXMLSchema()){
						del("学号", stu.getId());
						msg = "内容不符Schema规范，新增失败！";
					}else{
						f = true;
						msg = "新增成功！";
					}
						
					
	        }
	        
		} catch (DocumentException e1) {
			// TODO Auto-generated catch block
			f = false;
			System.out.println("新增时"+e1);
		}
		return msg;
	}

	//更新函数，同新增函数，但需先提取原节点保存，再更新验证，若验证不通过则写入原节点，返回反馈
	public String update(Student stu) {
		// TODO Auto-generated method stub
		try {
			doc = reader.read(new File(path));
			root = doc.getRootElement();
		} catch (DocumentException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		
		Student keepStu = new Student();//保存原节点信息
        List<Element> nodes = root.elements("学生成绩"); //学生成绩节点
        for(Element ele:nodes){
        	
        	if(ele.elementTextTrim("学号").equals(stu.getId())){
        		List<Element> eles = ele.elements();//学生成绩下各信息节点
        		for(Element e:eles){
					if(e.getName().equals("学号")){
						keepStu.setId(e.getTextTrim());
						e.setText(stu.getId());
					}else if("姓名".equals(e.getName())){
						keepStu.setName(e.getTextTrim());
						e.setText(stu.getName());
					}else if("JSP".equals(e.getName())){
						keepStu.setJSP(e.getTextTrim());
						e.setText(stu.getJSP());
					}else if("XML".equals(e.getName())){
						keepStu.setXML(e.getTextTrim());
						e.setText(stu.getXML());
					}
        		}
        	}
        }
        
        OutputFormat out = new OutputFormat("  ",true);
        out.setEncoding("UTF-8");
		
		try {
			Writer wr = new FileWriter(new File(path));
			XMLWriter xwr = new XMLWriter(wr,out);
			xwr.write(doc);
			wr.close();
			xwr.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}   
		
		if(!validateXMLSchema()){
			f = true;
			System.out.println("验证中不成功");
			msg = "内容不符Schema规范，修改失败！";
			restore(keepStu);
		}else{
			f = false;
			System.out.println("验证中成功");
			msg = "修改成功！";
		}
		
		if(f){
			f = false;
			System.out.println("f中不成功");
			return "内容不符Schema规范，修改失败！";
		}else{
			System.out.println("f中成功");
			return "修改成功！";
		}
	}

	//回档函数，在更新函数不通过schema验证时启用
	public void restore(Student stu) {
		// TODO Auto-generated method stub
		try {
			doc = reader.read(new File(path));
			root = doc.getRootElement();
		} catch (DocumentException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		
        List<Element> nodes = root.elements("学生成绩"); //学生成绩节点
        for(Element ele:nodes){
        	
        	if(ele.elementTextTrim("学号").equals(stu.getId())){
        		List<Element> eles = ele.elements();//学生成绩下各信息节点
        		for(Element e:eles){
					if(e.getName().equals("学号")){
						e.setText(stu.getId());
					}else if("姓名".equals(e.getName())){
						e.setText(stu.getName());
					}else if("JSP".equals(e.getName())){
						e.setText(stu.getJSP());
					}else if("XML".equals(e.getName())){
						e.setText(stu.getXML());
					}
        		}
        	}
        }
        
        OutputFormat out = new OutputFormat("  ",true);
        out.setEncoding("UTF-8");
		
		try {
			Writer wr = new FileWriter(new File(path));
			XMLWriter xwr = new XMLWriter(wr,out);
			xwr.write(doc);
			wr.close();
			xwr.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} 
		
	}
	
	//搜索函数，运用xpath技术进行模糊搜索
	public ArrayList<Student> search(String tag,String text,int currentPage ) throws ParserConfigurationException, SAXException, IOException, XPathExpressionException {
		// TODO Auto-generated method stub
	  DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();  
      factory.setNamespaceAware(false);  
      DocumentBuilder builder = factory.newDocumentBuilder();  
      org.w3c.dom.Document doc = builder.parse(path);  
      XPathFactory xFactory = XPathFactory.newInstance();  
      XPath xpath = xFactory.newXPath(); 
      
      ArrayList<Student> stus = new ArrayList<Student>();
      
      String Path1 = "//学生成绩[contains("+tag+",'"+text+"')]/姓名";
      String Path2 = "//学生成绩[contains("+tag+",'"+text+"')]/学号";
      String Path3 = "//学生成绩[contains("+tag+",'"+text+"')]/JSP";
      String Path4 = "//学生成绩[contains("+tag+",'"+text+"')]/XML";//不同的tag（搜索的节点名）再和tag下的文本匹配
      
      XPathExpression expr = xpath.compile(Path1);
      NodeList nameNodes = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);  
      
      expr = xpath.compile(Path2);
      NodeList idNodes = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
     
      expr = xpath.compile(Path3);
      NodeList jspNodes = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
   
      expr = xpath.compile(Path4);
      NodeList xmlNodes = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
   
      for(int i=(currentPage-1)*size;i<((currentPage-1)*size+size)&&i<nameNodes.getLength();i++){
    	  Student stu = new Student();
    	  stu.setId(idNodes.item(i).getTextContent());
    	  stu.setName(nameNodes.item(i).getTextContent());
    	  stu.setJSP(jspNodes.item(i).getTextContent());
    	  stu.setXML(xmlNodes.item(i).getTextContent());
    	  stus.add(stu);
      }  

		return stus ;
	}  
	
	//验证xml是否符合scheme规范
	public boolean validateXMLSchema(){

        try {
            SchemaFactory factory = 
                    SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
            Schema schema = factory.newSchema(new File(xsdPath));
            Validator validator = schema.newValidator();
            validator.validate(new StreamSource(new File(path)));
        } catch (Exception e) {
            System.out.println("验证错误: "+e.getMessage());
            return false;
        }
        return true;
    }
}
